
/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


#ifndef _NGX_PALLOC_H_INCLUDED_
#define _NGX_PALLOC_H_INCLUDED_


#include <ngx_config.h>
#include <ngx_core.h>

// 如果编译时指定宏NGX_DEBUG_PALLOC
// 则不会启用内存池机制，都使用malloc分配内存
// 方便使用valgrind等来检测内存问题

/*
 * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
 * On Windows NT it decreases a number of locked pages in a kernel.
 */
// 在内存池可直接分配的最大块，通常是4k-1
// ngx_pagesize 通常是系统内存页的大小（如 4096 字节）。
// -1 是为了对齐和安全考虑。
// 大于这个值的内存请求不会在池的内部块中分配，而是通过 large 链表单独分配（例如用 malloc）。
#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)

// 定义调用 ngx_create_pool 时如果不指定大小，默认创建的内存池大小（16KB）。
// 默认一个内存池块大小是16k
// 用于cycle->pool
// 注意，默认池大小与pagesize无关
#define NGX_DEFAULT_POOL_SIZE    (16 * 1024)

// 定义内存池中内存对齐的字节数（16字节，128位）
#define NGX_POOL_ALIGNMENT       16

// 计算并定义内存池的最小可能大小（内存池最小的大小）
// 首先要能够容纳ngx_pool_t结构体
// 然后还要至少能分配两个大内存块
// 最后16字节对齐
// 用于配置内存池时的参数检查
#define NGX_MIN_POOL_SIZE                                                     \
    ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)),            \
              NGX_POOL_ALIGNMENT)
// 一个有效的内存池必须至少能容纳下 ngx_pool_t 结构本身和两个 ngx_pool_large_t 结构（推测是为 large 链表预留初始空间），
// 并且这个大小还要按照 NGX_POOL_ALIGNMENT 进行对齐。
// ngx_align 宏确保计算出的最小大小是对齐的。


// 内存池销毁时调用的清理函数
// 相当于析构函数，必要的清理动作
// 这是一个函数指针，接受一个 void* 参数（数据），无返回值。
typedef void (*ngx_pool_cleanup_pt)(void *data);

typedef struct ngx_pool_cleanup_s  ngx_pool_cleanup_t;

// 清理结构体，类似于lambda，绑定了函数指针和参数
// ngx_pool_t::cleanup指向所有清理函数
struct ngx_pool_cleanup_s {
    ngx_pool_cleanup_pt   handler;  // 清理时调用的函数
    void                 *data;     // 传递给handler函数的数据
    ngx_pool_cleanup_t   *next;     // 指向下一个清理句柄，形成链表
};
// 内存池不仅管理内存生命周期，还管理资源的生命周期。
// ngx_pool_cleanup_t 允许你注册一个清理函数 (handler)，
// 当池被销毁时，这些函数会被调用，用于释放文件描述符、关闭文件、删除临时文件等操作。
// next 指针将所有清理句柄串联成一个链表。


// 大块内存节点
typedef struct ngx_pool_large_s  ngx_pool_large_t;

// 定义大内存块结构体
// 大块内存节点, 大于4k
// 保存成链表方便回收利用
struct ngx_pool_large_s {
    ngx_pool_large_t     *next;     // 指向下一个大内存块，形成链表
    void                 *alloc;    // 指向实际通过malloc等分配的大块内存地址
};
// 对于超过 NGX_MAX_ALLOC_FROM_POOL 的请求，内存池会单独分配一大块内存（例如用 malloc）。
// ngx_pool_large_t 结构体用来管理这些大块内存。
// alloc 指向真实内存，next 将所有大内存块串联成一个链表，方便在池销毁时统一遍历释放。

// 定义内存池数据块结构（管理池内部的内存分配）
// 一个内存池由多个 ngx_pool_t 结构通过 next 链接而成。
// 每个 ngx_pool_t 都包含一个 ngx_pool_data_t 来管理它自身所代表的那块内存。
// 64位系统大小为32字节
typedef struct {
    u_char               *last; // 指向当前内存块中下一个可分配空间的起始位置
    u_char               *end;  // 指向当前内存块的末尾
    ngx_pool_t           *next; // 指向下一个内存池块，形成链表

    // 本节点分配失败次数
    // 失败超过4次则本节点认为满，不再参与分配
    // 注意满的内存块不会主动回收
    ngx_uint_t            failed;   // 记录该内存块分配失败的次数（后续分配会跳过失败次数多的块）
    // failed 是一个优化指标。
    // 如果从一个块分配多次失败，current 指针会指向更新的块，避免每次都遍历失败率高的旧块。
} ngx_pool_data_t;

// nginx内存池结构体（内存池的完整抽象）
// 实际是由多个节点串成的单向链表
// 每个节点分配小块内存
// 但max、current、大块内存链表只在头节点
// 64位系统大小为80字节
// 结构体里没有保存块大小的字段，由d.end-p得到
struct ngx_pool_s {
    // 内存块数据管理结构，描述本内存池节点的信息
    ngx_pool_data_t       d;

    // 下面的字段仅在第一个块中有意义
    // 其他块中不存在，被用于分配内存

    // 可分配的最大块
    // 不能超过NGX_MAX_ALLOC_FROM_POOL,即4k-1
    // 基于 d.end - d.last 计算，表示本块剩余空间，用于快速判断请求是否超出本块能力
    size_t                max;

    // 当前使用的内存池节点
    // 指向当前可用于分配的内存池块（跳过failed过多的块）
    ngx_pool_t           *current;

    // 为chain做的优化，空闲缓冲区链表
    ngx_chain_t          *chain;

    // 大块的内存，串成链表
    ngx_pool_large_t     *large;

    // 清理链表头指针
    ngx_pool_cleanup_t   *cleanup;

    // 关联的日志对象，用于记录分配失败等日志
    ngx_log_t            *log;
};


// 定义一个专门用于文件清理的数据结构
typedef struct {
    ngx_fd_t              fd;   // 文件描述符
    u_char               *name; // 文件名（用于删除文件等清理操作）
    ngx_log_t            *log;  // 日志对象
} ngx_pool_cleanup_file_t;


ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
void ngx_destroy_pool(ngx_pool_t *pool);
void ngx_reset_pool(ngx_pool_t *pool);

void *ngx_palloc(ngx_pool_t *pool, size_t size);
void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);


ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);
void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);
void ngx_pool_cleanup_file(void *data);
void ngx_pool_delete_file(void *data);


#endif /* _NGX_PALLOC_H_INCLUDED_ */
